package org.infinispan.marshall.exts;
import org.infinispan.commons.marshall.AdvancedExternalizer;
import org.infinispan.commons.marshall.MarshallUtil;
import org.infinispan.commons.util.Util;
import org.infinispan.distribution.util.ReadOnlySegmentAwareCollection;
import org.infinispan.marshall.core.Ids;
import org.jboss.marshalling.util.IdentityIntMap;
import java.io.IOException;
import java.io.ObjectInput;
import java.io.ObjectOutput;
import java.util.ArrayDeque;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashSet;
import java.util.LinkedList;
import java.util.Set;
import java.util.TreeSet;
public class CollectionExternalizer implements AdvancedExternalizer<Collection> {
private static final int ARRAY_LIST = 0;
private static final int LINKED_LIST = 1;
private static final int SINGLETON_LIST = 2;
private static final int EMPTY_LIST = 3;
private static final int HASH_SET = 4;
private static final int TREE_SET = 5;
private static final int SINGLETON_SET = 6;
private static final int SYNCHRONIZED_SET = 7;
private static final int ARRAY_DEQUE = 8;
private static final int READ_ONLY_SEGMENT_AWARE_COLLECTION = 9;
private final IdentityIntMap<Class<?>> numbers = new IdentityIntMap<>(16);
public CollectionExternalizer() {
numbers.put(ArrayList.class, ARRAY_LIST);
numbers.put(getPrivateArrayListClass(), ARRAY_LIST);
numbers.put(getPrivateUnmodifiableListClass(), ARRAY_LIST);
numbers.put(LinkedList.class, LINKED_LIST);
numbers.put(getPrivateSingletonListClass(), SINGLETON_LIST);
numbers.put(getPrivateEmptyListClass(), EMPTY_LIST);
numbers.put(ArrayDeque.class, ARRAY_DEQUE);
numbers.put(HashSet.class, HASH_SET);
numbers.put(TreeSet.class, TREE_SET);
numbers.put(getPrivateSingletonSetClass(), SINGLETON_SET);
numbers.put(getPrivateSynchronizedSetClass(), SYNCHRONIZED_SET);
numbers.put(getPrivateUnmodifiableSetClass(), HASH_SET);
numbers.put(ReadOnlySegmentAwareCollection.class, READ_ONLY_SEGMENT_AWARE_COLLECTION);
}
@Override
public void writeObject(ObjectOutput output, Collection collection) throws IOException {
int number = numbers.get(collection.getClass(), -1);
output.writeByte(number);
switch (number) {
case ARRAY_LIST:
case LINKED_LIST:
case HASH_SET:
case SYNCHRONIZED_SET:
case ARRAY_DEQUE:
case READ_ONLY_SEGMENT_AWARE_COLLECTION:
MarshallUtil.marshallCollection(collection, output);
break;
case SINGLETON_LIST:
case SINGLETON_SET:
output.writeObject(collection.iterator().next());
break;
case TREE_SET:
output.writeObject(((TreeSet) collection).comparator());
MarshallUtil.marshallCollection(collection, output);
break;
}
}
@Override
public Collection readObject(ObjectInput input) throws IOException, ClassNotFoundException {
int magicNumber = input.readUnsignedByte();
switch (magicNumber) {
case ARRAY_LIST:
return MarshallUtil.unmarshallCollection(input, ArrayList::new);
case LINKED_LIST:
return MarshallUtil.unmarshallCollectionUnbounded(input, LinkedList::new);
case SINGLETON_LIST:
return Collections.singletonList(input.readObject());
case EMPTY_LIST:
return Collections.emptyList();
case HASH_SET:
return MarshallUtil.unmarshallCollection(input, s -> new HashSet<>());
case TREE_SET:
Comparator<Object> comparator = (Comparator<Object>) input.readObject();
return MarshallUtil.unmarshallCollection(input, s -> new TreeSet<>(comparator));
case SINGLETON_SET:
return Collections.singleton(input.readObject());
case SYNCHRONIZED_SET:
return Collections.synchronizedSet(
MarshallUtil.unmarshallCollection(input, s -> new HashSet<>()));
case ARRAY_DEQUE:
return MarshallUtil.unmarshallCollection(input, ArrayDeque::new);
case READ_ONLY_SEGMENT_AWARE_COLLECTION:
return MarshallUtil.unmarshallCollection(input, ArrayList::new);
default:
throw new IllegalStateException("Unknown Set type: " + magicNumber);
}
}
@Override
public Integer getId() {
return Ids.COLLECTIONS;
}
@Override
public Set<Class<? extends Collection>> getTypeClasses() {
return Util.asSet(ArrayList.class, LinkedList.class,
getPrivateArrayListClass(),
getPrivateUnmodifiableListClass(),
getPrivateSingletonListClass(),
getPrivateEmptyListClass(),
HashSet.class, TreeSet.class,
getPrivateSingletonSetClass(),
getPrivateSynchronizedSetClass(), getPrivateUnmodifiableSetClass(),
ArrayDeque.class,
ReadOnlySegmentAwareCollection.class);
}
private static Class<Collection> getPrivateArrayListClass() {
return getCollectionClass("java.util.Arrays$ArrayList");
}
private static Class<Collection> getPrivateUnmodifiableListClass() {
return getCollectionClass("java.util.Collections$UnmodifiableRandomAccessList");
}
private static Class<Collection> getPrivateEmptyListClass() {
return getCollectionClass("java.util.Collections$EmptyList");
}
private static Class<Collection> getPrivateSingletonListClass() {
return getCollectionClass("java.util.Collections$SingletonList");
}
public static Class<Collection> getPrivateSingletonSetClass() {
return getCollectionClass("java.util.Collections$SingletonSet");
}
public static Class<Collection> getPrivateSynchronizedSetClass() {
return getCollectionClass("java.util.Collections$SynchronizedSet");
}
private static Class<Collection> getPrivateUnmodifiableSetClass() {
return getCollectionClass("java.util.Collections$UnmodifiableSet");
}
private static Class<Collection> getCollectionClass(String className) {
return Util.<Collection>loadClass(className, Collection.class.getClassLoader());
}
}